S14-02 SSR-Nuxt3
[TOC]
API
h3
- defineEventHandler()
- Request
- getQuery()
- getRequestURL()
- getRouterParams()
- isMethod()
- readBody()
- Response
概述
什么是 Nuxt
Nuxt 官方文档:https://nuxt.com.cn/docs/getting-started/installation
在了解 Nuxt 之前,我们先来了解一下创建一个现代应用程序,所需的技术:
- 支持数据双向绑定和组件化( Nuxt 选择了 Vue.js )。
- 处理客户端的导航( Nuxt 选择了 vue-router )。
- 支持开发中热模块替换和生产环境代码打包( Nuxt 支持 webpack 5 和 Vite )。
- 兼容旧版浏览器,支持最新的 JavaScript 语法转译( Nuxt 使用 esbuild )。
- 应用程序支持开发环境服务器,也支持服务器端渲染 或 API 接口开发。
- Nuxt 使用 h3 来实现部署可移植性(h3 是一个极小的高性能的 http 框架)
- 如:支持在 Serverless、Workers 和 Node.js 环境中运行。
Nuxt是一个直观的 Web 框架
- 自 2016 年 10 月以来,Nuxt 专门负责集成上述所描述的事情 ,并提供前端和后端的功能。
- Nuxt 框架可以用来快速构建一个 Vue.js 应用程序,如支持 CSR 、SSR、SSG 渲染模式的应用等。
Nuxt 发展史
Nuxt.js:
- 诞生于 2016 年 10 月 25 号,由 Sebastien Chopin 创建,主要是基于 Vue2 、Webpack2 、Node 和 Express。
- 在 2018 年 1 月 9 日, Sebastien Chopin 正式宣布,发布 Nuxt.js 1.0 版本。
- 重要的变化是放弃了对 node < 8 的支持
- 2018 年 9 月 21 日, Sebastien Chopin 正式宣布,发布 Nuxt.js 2.0 版本。
- 开始使用 Webpack 4 及其技术栈, 其它的并没有做出重大更改。
- 2021 年 8 月 12 日至今,Nuxt.js 最新的版本为:Nuxt.js 2.15.8
Nuxt3:
- 经过 16 个月的工作,Nuxt 3 beta 于 2021 年 10 月 12 日发布,引入了基于 Vue3、Vite 和 Nitro( 服务引擎 ) 。
- 六个月后,2022 年 4 月 20 日,Pooya Parsa 宣布 Nuxt3 的第一个候选版本,代号为“Mount Hope”。
- 在 2022 年 11 月 16 号, Pooya Parsa 再次宣布 Nuxt3 发布为第一个正式稳定版本。
Nuxt3 特点
Vue 技术栈:
- Nuxt3 是基于 Vue3 + Vue Router + Vite 等技术栈,全程 Vue3+Vite 开发体验(Fast)。
自动导包:
- Nuxt 会自动导入辅助函数、组合 API 和 Vue API ,无需手动导入。
- 基于规范的目录结构,Nuxt 还可以对自己的组件、 插件使用自动导入。
约定式路由:
- Nuxt 路由基于 vue-router,在 pages 目录中创建的每个页面,都会根据目录结构和文件名来自动生成路由。
渲染模式:
Nuxt 支持多种渲染模式(SSR、CSR、SSG 等)
利于搜索引擎优化:
- 服务器端渲染模式,不但可以提高首屏渲染速度,还利于 SEO
服务器引擎:
在开发环境中,它使用 Rollup 和 Node.js 。
在生产环境中,使用 Nitro 将您的应用程序和服务器构建到一个通用
.output
目录中。Nitro服务引擎提供了跨平台部署的支持,包括 Node、Deno、Serverless、Workers 等平台上部署。
Nuxt.js VS Nuxt3
基础
环境搭建
前置环境:
- Node.js:v18.0.0 或更新版本,推荐使用偶数版本。
- 文本编辑器:我们推荐使用 Visual Studio Code 以及 Volar(Vue - Official) 扩展
- 终端:用于运行 Nuxt 命令
新建项目:
1、在命令行中,运行以下命令:
# pnpm
pnpm dlx nuxi init <project-name>
# npm
npx nuxi init <project-name>
2、进入到<project-name>
目录,安装依赖。
pnpm install
# 以前需要携带以下参数,用来创建一个扁平的node_modules目录结构
# pnpm install --shamefully-hoist
错误: 执行npx nuxi init 01-hello-nuxt
报如下错误:
分析:主要是网络不通导致。
解决:配置 host 翻墙
1、ping raw.githubusercontent.com
检查是否通。如果访问不通,代表是网络不通。
2、配置 host,本地解析域名:
- Mac 电脑 host 配置路径:
/etc/hosts
- Win 电脑 host 配置路径:
c:/Windows/System32/drivers/etc/hosts
3、在 host 文件中新增一行 ,编写如下配置:
185.199.108.133 raw.githubusercontent.com
# 或者以下其中一个
185.199.108.133 raw.githubusercontent.com
185.199.109.133 raw.githubusercontent.com
185.199.110.133 raw.githubusercontent.com
185.199.111.133 raw.githubusercontent.com
4、重新 ping 域名,如果通了就可以用了。
5、重新开一个终端创建项目即可。
目录结构
package.json 脚本:
注意: postinstall
是一种 npm 生命周期脚本,在安装依赖包后自动执行。它可以用于执行一些初始化任务,比如构建项目、复制文件或设置环境变量。
"scripts": {
"postinstall": "node setup.js"
}
app.vue
默认情况下,Nuxt 会将此文件视为入口,并为应用程序的每个路由呈现其内容。
作用:
- 布局:定义页面布局 Layout 或 自定义布局,如:
<NuxtLayout>
- 路由占位:定义路由的占位,如:
<NuxtPage>
- 编写全局样式
- 全局监听路由
配置
nuxt.config▸
nuxt.config.ts
配置文件位于项目的根目录,可对 Nuxt 进行自定义配置。
Nuxt 配置:
runtimeConfig:
{ public, 自定义 }
,运行时配置,即定义环境变量。支持服务器和客户端的变量。注意: 配置会被
.env
配置覆盖。public:
{ 自定义 }
,这个部分的配置会被公开到客户端,因此可以在前端和服务端代码中都可以安全使用。- 自定义:``,自定义的环境变量。
...自定义:``,自定义的私有环境变量。这个部分的配置仅在服务器端可用,不能被客户端访问。
js// 1. 在runtimeConfig 中定义环境变量 runtimeConfig: { public: { apiUrl: process.env.API_URL, xxx: 'http://baidu.com' }, yyy: 'yyy', zzz: 'zzz' },
js// 2. 访问环境变量:useRuntimeConfig() const config = useRuntimeConfig() // 在客户端 if (process.client) { const apiUrl = config.public.apiUrl } // 在服务器端 if (process.server) { const apiUrl = config.public.apiUrl const secret = config.yyy }
appConfig:
{ 自定义 }
,应用配置,定义在构建时确定的公共变量,如:theme。注意: 配置会和
app.config.ts
配置合并。优先级:app.config.ts > appConfig
。注意: 前后端都可以访问配置项。
jsexport default defineNuxtConfig({ appConfig: { siteName: 'My Awesome Site', apiBase: process.env.API_BASE_URL || 'https://api.example.com' } })
js// 访问配置:useAppConfig() const appConfig = useAppConfig() console.log(appConfig.siteName) // 输出: My Awesome Site console.log(appConfig.apiBase) // 输出: https://api.example.com
app:
{baseURL, head}
,应用相关的配置,如头部信息、全局样式等。baseURL:
string
,定义应用的基础 URL,通常用于处理相对路径。buildAssetsDir:
string
,用于自定义构建生成的静态资产的目录。cdnURL:
string
,用于指定 CDN(内容分发网络)的基本 URL,这样你可以将静态资源托管在 CDN 上,提高加载速度。head:
{title, meta, link, style, script}
,给每个页面上设置 head 信息,也支持 useHead 配置和内置组件。title:
string
,标题。meta:
[{charset},{ name, content}]
,元数据。{charset}
:``,字符编码。{ name, content}
:``,
link:
[{rel, href}]
,外链样式。style:
[{children, type}]
,样式。script:
[{src, body?}]
,JS 脚本。- src:``,script 连接地址。
- body?:
boolean
,是否将 script 添加到 body 标签中。
jsapp: { baseURL: '/my-app/', buildAssetsDir: 'static/assets/', // 自定义静态资源目录 cdnURL: 'https://cdn.example.com/', // 替换为你的CDN地址 head: { meta: [ // <meta name="viewport" content="width=device-width, initial-scale=1"> { name: 'viewport', content: 'width=device-width, initial-scale=1' } ], script: [ // <script src="https://myawesome-lib.js"></script> { src: 'https://awesome-lib.js' } ], link: [ // <link rel="stylesheet" href="https://myawesome-lib.css"> { rel: 'stylesheet', href: 'https://awesome-lib.css' } ], // please note that this is an area that is likely to change style: [ // <style type="text/css">:root { color: red }</style> { children: ':root { color: red }', type: 'text/css' } ], noscript: [ // <noscript>JavaScript is required</noscript> { children: 'JavaScript is required' } ] } }
ssr:
boolean
,默认:true
,指定应用的渲染模式。true
:开启 SSR 渲染。false
:关闭 SSR 渲染,此时属于 SPA 应用。
router:
{base, middleware, options}
,配置路由相关的信息,比如在客户端渲染可以配置 hash 路由。base:
string
,用于设置应用的基础路径。如果你的应用部署在一个子目录下,可以通过这个选项来指定。middleware:
string[]
,指定全局中间件,可在每个页面的渲染之前执行一些逻辑,如身份验证等。options:
{hashMode}
,- hashMode:
boolean
,是否使用 hash 路由(只在 SPA 应用中有效)。开启 hash 路由
jsrouter: { base: '/app/', middleware: ['auth'], options: { hashMode: true // hash路由 } },
js// 开启hash路由 export default defineNuxtConfig({ ssr: false, router: { options: { hashMode: true } } })
- hashMode:
css:
string[]
,全局 CSS 文件。jscss: ['@/assets/styles/global.css'],
modules:
string[]
,引入 Nuxt 模块,增强功能。jsmodules: ['@nuxtjs/axios', '@nuxtjs/pwa'],
plugins:
[{src, mode}]
,定义插件,支持客户端和服务端。src:``,
mode:``,
jsplugins: [{ src: '~/plugins/myPlugin.js', mode: 'client' }],
routeRules:``,定义路由规则,可更改路由的渲染模式或分配基于路由缓存策略(公测阶段)
- :``,
middleware:
string[]
,定义全局中间件。jsrouter: { middleware: ['auth'], },
alias:``,路径的别名,默认已配好。
- :``,
build:
{extractCSS, extend(config, { isDev })}
,Webpack 构建相关配置。extractCSS:``,
extend(config, { isDev }):``,
jsbuild: { extractCSS: true, extend(config, { isDev }) { if (isDev) { config.devtool = 'source-map'; } }, },
builder:``,可指定用 vite 还是 webpack 来构建应用,默认是 vite。如切换为 webpack 还需要安装额外的依赖。
- :``,
i18n:
{locales, defaultLocale}
,配置国际化支持。locales:
[{code, name}]
,- code:``,
- name:``,
defaultLocale:
string
,jsi18n: { locales: [ { code: 'en', name: 'English' }, { code: 'fr', name: 'Français' }, ], defaultLocale: 'en', },
runtimeConfig
runtimeConfig:
{ public, 自定义 }
,运行时配置,即定义环境变量。支持服务器和客户端的变量。注意: 配置会被
.env
配置覆盖。public:
{ 自定义 }
,这个部分的配置会被公开到客户端,因此可以在前端和服务端代码中都可以安全使用。- 自定义:``,自定义的环境变量。
...自定义:``,自定义的私有环境变量。这个部分的配置仅在服务器端可用,不能被客户端访问。
js// 1. 在runtimeConfig 中定义环境变量 runtimeConfig: { public: { apiUrl: process.env.API_URL, xxx: 'http://baidu.com' }, yyy: 'yyy', zzz: 'zzz' },
js// 2. 访问环境变量:useRuntimeConfig() const config = useRuntimeConfig() // 在客户端 if (process.client) { const apiUrl = config.public.apiUrl } // 在服务器端 if (process.server) { const apiUrl = config.public.apiUrl const secret = config.yyy }
定义环境变量的方式:
方式一:在nuxt.config.ts
中配置runtimeConfig
访问环境变量:
方式二:在.env
中配置环境变量
可以通过.env
文件中的环境变量来覆盖runtimeConfig
中的配置,优先级:.env > runtimeConfig
。
- .env 的变量会添加到 process.env 中,符合规则的会覆盖 runtimeConfig 的变量。
- .env 一般用于某些终端启动应用时动态指定配置,不区分 dev 和 pro 环境。
注意:
注意常量写法和驼峰写法之间的对应关系:
NUXT_APP_KEY
-appKey
NUXT_PUBLIC_BASE_URL
-public.baseURL
在
.env
中定义的环境变量必须在nuxt.config
中定义。这样可以确保任意的环境变量不会暴露给应用程序代码。
获取.env 文件中的环境变量:
和方式一一样,也是通过 useRuntimeConfig()
获取环境变量。
注意: 无法通过 process.env
获取。可能需要 dotenv 插件。
appConfig
appConfig:
{ 自定义 }
,应用配置,定义在构建时确定的公共变量,如:theme。注意: 配置会和
app.config.ts
配置合并。优先级:app.config.ts > appConfig
。注意: 前后端都可以访问配置项。
jsexport default defineNuxtConfig({ appConfig: { siteName: 'My Awesome Site', apiBase: process.env.API_BASE_URL || 'https://api.example.com' } })
js// 访问配置:useAppConfig() const appConfig = useAppConfig() console.log(appConfig.siteName) // 输出: My Awesome Site console.log(appConfig.apiBase) // 输出: https://api.example.com
抽取配置到app.config.ts
创建app.config.ts
文件,并配置如下:
app
app:
{baseURL, head}
,应用相关的配置,如头部信息、全局样式等。baseURL:
string
,定义应用的基础 URL,通常用于处理相对路径。buildAssetsDir:
string
,用于自定义构建生成的静态资源的目录。cdnURL:
string
,用于指定 CDN(内容分发网络)的基本 URL,这样你可以将静态资源托管在 CDN 上,提高加载速度。head:
{title, meta, link, style, script}
,给每个页面上设置 head 信息,也支持 useHead 配置和内置组件。title:
string
,标题。meta:
[{charset},{ name, content}]
,元数据。{charset}
:``,字符编码。{ name, content}
:``,
link:
[{rel, href}]
,外链样式。style:
[{children, type}]
,样式。script:
[{src, body?}]
,JS 脚本。- src:``,script 连接地址。
- body?:
boolean
,是否将 script 添加到 body 标签中。
jsapp: { baseURL: '/my-app/', buildAssetsDir: 'static/assets/', // 自定义静态资源目录 cdnURL: 'https://cdn.example.com/', // 替换为你的CDN地址 head: { meta: [ // <meta name="viewport" content="width=device-width, initial-scale=1"> { name: 'viewport', content: 'width=device-width, initial-scale=1' } ], script: [ // <script src="https://myawesome-lib.js"></script> { src: 'https://awesome-lib.js' } ], link: [ // <link rel="stylesheet" href="https://myawesome-lib.css"> { rel: 'stylesheet', href: 'https://awesome-lib.css' } ], // please note that this is an area that is likely to change style: [ // <style type="text/css">:root { color: red }</style> { children: ':root { color: red }', type: 'text/css' } ], noscript: [ // <noscript>JavaScript is required</noscript> { children: 'JavaScript is required' } ] } }
应用:
- 可以在 app 选项中做 SEO 优化配置。
添加 app 配置的方式:
优先级:内置组件
> useHead()
> nuxt.config.ts
方式一: 使用useHead()
钩子函数动态添加 app 配置项。
- 如果在
app.vue
中使用useHead()
会在项目全局起作用。 - 如果在某个
vue
文件中使用,则只会在该页面起作用。
方式二: 使用<Head>
、<Meta>
、<Style>
、<Link>
、<Script>
这些内置组件添加 app 配置项。
方式三: 在nuxt.config.ts
配置的app
配置项中设置。
app.config
app.config.ts
用于公开在构建时确定的公共变量。
注意:
- app.config.ts 配置文件中的选项不能使用 env 环境变量来覆盖,与 runtimeConfig 不同。
- 不要存放秘密或敏感信息在 app.config.ts 文件中,该文件是客户端公开
配置: 使用辅助函数 defineAppConfig()
提供代码提示
export default defineAppConfig({
title: 'Hello Nuxt',
theme: {
dark: true,
colors: {
primary: '#ff0000'
}
}
})
访问配置:
// pages/index.vue
const appConfig = useAppConfig()
console.log(appConfig.theme.dark)
应用场景:
- 全局常量:用于存放网站名称、主题色等不变的全局信息。
- API 配置:方便管理 API 的基础 URL,便于调用。
- 主题设置:可以集中管理主题相关的配置,比如颜色、字体等。
- 功能开关:可以用于管理不同环境下的功能开关。
runtimeConfig 与 app.config
runtimeConfig 和 app.config 都用于向应用程序公开变量。要确定是否应该使用其中一种,以下是一些指导原则:
- runtimeConfig:定义环境变量,比如:运行时需要指定的私有或公共 token。
- app.config:定义公共变量,比如:在构建时确定的公共 token、网站配置。
内置组件
Nuxt3 框架也提供一些内置的组件,常用的如下:
- SEO 组件: Html、Body、Head、Title、Meta、Style、Link、NoScript、Base
- NuxtWelcome:欢迎页面组件,该组件是 @nuxt/ui 的一部分
- NuxtLayout:是 Nuxt 自带的页面布局组件
- NuxtPage:是 Nuxt 自带的页面占位组件
- 需要显示位于目录中的顶级或嵌套页面 pages/
- 是对 router-view 的封装
- ClientOnly:该组件中的默认插槽的内容只在客户端渲染。而 fallback 插槽的内容只在服务器端渲染。
- NuxtLink:是 Nuxt 自带的页面导航组件
- 是 Vue Router
<RouterLink>
组件 和 HTML<a>
标签的封装。
- 是 Vue Router
- 等等
NuxtPage
NuxtPage:是 Nuxt 自带的页面占位组件。<NuxtPage>
是对 Vue Router 的 <RouterView>
组件的封装。
1、在app.vue
中使用占位组件<NuxtPage>
2、在/pages/
目录下创建组件home.vue
和index.vue
3、直接输入路由地址:localhost:3000
访问index.vue
,localhost:3000/home
访问home.vue
ClientOnly
ClientOnly:该组件中的默认插槽的内容只在客户端渲染。而 fallback 插槽的内容只在服务器端渲染。
加载过程中显示 fallback 渲染的内容,等加载完毕就会用客户端渲染的内容替换 fallback 渲染的内容。
- ClientOnly:
()
,只在客户端渲染其插槽内容。- 属性
- fallback:``,渲染占位
- fallback-tag:``,渲染占位标签
- 插槽
- fallback:``,指定在服务器端显示的内容。
示例:
<ClientOnly fallback-tag="span" fallback="加载评论中...">
<!-- 这将在客户端渲染 -->
<template #fallback>
<!-- 这将在服务器端渲染 -->
<p>加载评论中...</p>
</template>
</ClientOnly>
NuxtLink
NuxtLink:()
,用来实现页面的导航。
- 属性:
- to:
string
,指定要导航的目标路径。支持路由路径、路由对象、URL。 - href:
string
,to 的别名。 - active-class:
默认:nuxt-link-active
,激活链接的类名。 - target:
_blank | _self |_parent |_top
,与普通<a>
标签相同,指定链接打开方式。
全局样式
手动导入
依赖包:
- sass
- 安装:
pnpm i sass -D
- 安装:
基本使用:
1、在.css
或.scss
文件中编写样式
2、在nuxt.config.ts
的css中配置全局样式
3、此时可以在组件中直接使用全局样式。
使用变量或混入:
1、在.scss
文件中编写变量或混入
2、在全局或局部组件中引入.scss
文件并使用变量或混入
方式一:使用@import
引入
全局组件
局部组件
方式二:使用@use
引入(推荐)
自动导入▸
1、在.css
或.scss
文件中编写样式
2、在nuxt.config.ts
的vite中配置全局样式
注意: 此处导入的文件和css
配置项中导入的文件不能重复。
export default defineNuxtConfig({
css: ['~/assets/css/main.css'],
vite: {
css: {
preprocessorOptions: {
scss: {
// 在每个 SCSS 文件的开头自动引入多个scss文件。
additionalData: `
@use "@/assets/styles/global.scss" as *;
@use "@/assets/styles/variables.scss" as *;
@use "@/assets/styles/mixins.scss" as *;
`
}
}
}
}
})
3、此时可以在组件中直接使用全局样式。
资源导入
public 目录
public 目录用作应用程序的公共服务器,用于存放在应用程序的指定 URL 下公开访问的静态资源。
在 public/img/
目录中引用一个图像文件,该文件可通过静态 URL /img/nuxt.png
访问:
<template>
<img src="/img/nuxt.png" alt="Discover Nuxt 3" />
</template>
静态的 URL /img/nuxt.png
也支持在背景中使用:
<template>
<div class="box"></div>
</template>
<style lang="scss">
.box {
width: 200px;
height: 150px;
background-image: url(/img/nuxt.png);
}
</style>
assets 目录
assets 目录经常用于存放如样式表、字体或 SVG 的资源
可以使用 ~/assets/
路径引用位于 assets 目录中的资产文件
<template>
<img src="~/assets/img/nuxt.png" alt="Discover Nuxt 3" />
</template>
~/assets/
路径也支持在背景中使用
<template>
<div class="box"></div>
</template>
<style lang="scss">
.box {
width: 200px;
height: 150px;
background-image: url(~/assets/img/nuxt.png);
}
</style>
注意: 同样支持@/assets/
和base64
的写法。
<img src="@/assets/img/nuxt.png" alt="Discover Nuxt 3" />
<img src="base64;type=image/png xxx" alt="Discover Nuxt 3" />
字体图标
1、将字体图标存放在 assets 目录下
2、字体文件可以使用 ~/assets/
路径引用。
3、在 nuxt.config 配置文件中导入全局样式
4、在页面中就可以使用字体图标了
路由
新建页面
Nuxt 项目中的页面是在 pages 目录下创建的。
在 pages 目录创建的页面,Nuxt 会根据该页面的目录结构和其文件名来自动生成对应的路由。
页面路由也称为文件系统路由器(file system router),路由是 Nuxt 的核心功能之一。
手动创建页面:
- 1.创建页面文件,比如: pages/index.vue
- 2.将
<NuxtPage />
内置组件添加到 app.vue - 3.页面如果使用 scss 那么需要安装:
npm i sass -D
命令行创建页面:
语法:
pnpm dlx nuxi add page <path>
说明:
pnpm dlx nuxi
:相当于npx nuxi
add page
:创建页面的命令<path>
:要添加的页面路径,可以是相对路径。有以下写法:about
:创建pages/about.vue
页面。about/index
:创建pages/about/index.vue
页面。about/[id]
:创建pages/about/[id].vue
页面。about-[role]/[id]
:创建pages/about-[role]/[id].vue
页面。
示例:
npx nuxi add page home # 创建 home 页面
npx nuxi add page detail/[id] # 创建 detail 页面
npx nuxi add page user-[role]/[id] # 创建 user 页面
导航
组件导航
NuxtLink:()
,用来实现页面的导航。
属性:
to:
string | {path, query | params}
,指定要导航的目标路径。支持路由路径、路由对象、URL。href:
string
,to 的别名。active-class:
默认:nuxt-link-active
,激活链接的类名。target:
_blank | _self |_parent |_top
,与普通<a>
标签相同,指定链接打开方式。replace:
boolean
,如果设置为true
,则在导航时使用替换模式,这意味着不会在历史记录中添加新条目。prefetch:
boolean
,如果设置为true
,为进入视口的链接启用预加载资源。html<template> <nav> <NuxtLink to="/home">Home</NuxtLink> <NuxtLink :to="{path: '/home', query: {id: 10}}">Home</NuxtLink> <NuxtLink to="https://www.baidu.com">Home</NuxtLink> <NuxtLink href="/home">Home</NuxtLink> <NuxtLink to="/about" active-class="active">About</NuxtLink> <NuxtLink to="/profile" target="_blank">Profile</NuxtLink> <NuxtLink to="/profile" replace>Profile</NuxtLink> <NuxtLink to="/about" prefetch>About</NuxtLink> <!-- 使用a标签页面会刷新 --> <a href="/more">More</a> </nav> </template>
<NuxtLink>
是 Nuxt 内置组件,是对 <RouterLink>
的封装,用来实现页面的导航。
当<NuxtLink>
在客户端视口中可见时,Nuxt 会自动预取链接页面的组件和负载(生成的页面),从而加快导航速度。
该组件底层是一个<a>
标签,因此使用 a + href 属性也支持路由导航。但是用 a 标签导航会有触发浏览器默认刷新事件,而 NuxtLink 不会,NuxtLink 还扩展了其它的属性和功能。
应用 Hydration 后(已激活,可交互),页面导航会通过前端路由来实现。这可以防止整页刷新。
当然,手动输入 URL 后,点击刷新浏览器也可导航,这会导致整个页面刷新。
编程导航
navigateTo():
(to , options?)
,用于进行编程导航的函数。非常适合需要在事件处理程序或生命周期钩子中进行导航的场景。to:
string | {path, query | params}
,目标路由的路径,可以是一个字符串(如/about
),也可以是一个对象,包含更复杂的路由信息。options?:
{replace, external}
,用于控制导航的行为。- replace:
boolean
,默认:false
,为 true 时会替换当前路由页面。 - external:
boolean
,默认:false
,为 true 时允许导航到外部连接。
- replace:
返回:
promise:
Promise
,js// to:字符串路径 navigateTo('/about') // to:对象路径-query navigateTo({ path: '/home', query: { name: 'tom' } }) // to:对象路径-params navigateTo({ path: '/posts/[id]', params: { id: postId } }) // repalce选项:替换当前历史记录条目 navigateTo('/home', { replace: true }) // external选项:跳转到外部链接(必须设置external:true) navigateTo('https://www.baidu.com', { external: true })
useRouter():
()
,用于访问 Vue Router 实例。可以在组件中实现导航功能。router:
Router
,提供了一系列方法和属性,以便于进行路由导航和获取当前路由信息。jsimport { useRouter } from 'vue-router' const router = useRouter()
router.push():
(to)
,导航到一个新的路由。建议用 navigateTo ,支持性更好。to:
string | {path, query | params}
,jsrouter.push('/about')
router.replace():
(to)
,替换当前的路由,而不是添加新条目。建议用 navigateTo ,支持性更好。to:
string | {path, query | params}
,jsrouter.replace('/home')
router.go():
(count)
,前进或后退指定数量的历史记录条目。count:
number | -number
,jsrouter.go(-1) // 后退一步
router.back():
()
,回到上一个路由。:``,
jsrouter.back()
router.forward():
()
,页面前进。:``,
jsrouter.forward()
router.beforeEach():
((to, from, next) => void)
,注册全局前置守卫(通常在应用初始化时使用)。to:``,
from:``,
next:``,
jsrouter.beforeEach((to, from, next) => { // 逻辑 next() })
router.afterEach():
((to, from, next) => void)
,注册全局后置守卫。to:``,
from:``,
next:``,
jsrouter.afterEach((to, from, next) => { // 逻辑 next() })
navigateTo():
Nuxt3 除了可以通过<NuxtLink>
内置组件来实现导航,同时也支持编程导航:navigateTo() 。
通过编程导航,在应用程序中就可以轻松实现动态导航了,但是编程导航不利于 SEO。
navigateTo 函数在服务器端和客户端都可用,也可以在插件、中间件中使用,也可以直接调用以执行页面导航,例如:
- 当用户触发该 goToProfile()方法时,我们通过 navigateTo 函数来实现动态导航。
- 建议: goToProfile 方法总是返回 navigateTo 函数(该函数不需要导入)或 返回异步函数
useRouter():
Nuxt3 中的编程导航除了可以通过 navigateTo 来实现导航,同时也支持 useRouter() ( 或 Options API 的 this.$router )
动态路由
Nuxt3 和 Vue 一样,也是支持动态路由的,只不过在 Nuxt3 中,动态路由也是根据目录结构和文件的名称自动生成。
语法:
动态路由在 Nuxt 3 中通过在 pages
目录中使用方括号([]
)来定义。方括号里编写动态路由的参数。
# /detail/:id
pages/detail/[id].vue
# /detail/user-:id
pages/detail/user-[id].vue
# /detail/:role/:id
pages/detail/[role]/[id].vue
# /detail-:role/:id
pages/detail-[role]/[id].vue
路由参数
动态路由参数: route.params
1、通过 []
方括号语法定义动态路由,比如:/detail/[id].vue
2、页面跳转时,在 URL 路径中传递动态路由参数,比如:/detail/10010
3、目标页面通过 route.params
获取动态路由参数
import { useRoute } from 'vue-router'
const route = useRoute()
const userId = route.params.id // 获取动态路由参数
查询字符串参数: route.query
1、页面跳转时,通过查询字符串方式传递参数,比如:/detail/10010?name=liujun
2、目标页面通过 route.query
获取查询字符串参数
import { useRoute } from 'vue-router'
const route = useRoute()
const name = route.query.name // 获取查询字符串参数
全匹配路由
如果你需要一个全匹配路由,你可以创建一个名为 [...slug].vue
的文件。这将匹配该路径下的所有路由。
获取路由参数: 可以通过 $route.params 获取路由参数。
<!-- pages/[...slug].vue -->
<template>
<p>{{ $route.params.slug }}</p>
</template>
导航到 /hello/world
将渲染:
<p>["hello", "world"]</p>
注意:
[...slug].vue
文件除了支持在 pages 根目录下创建,也支持在其子目录中创建。- Nuxt3 正式版不支持 404.vue 页面了,以前的候选版是支持的 404.vue,但是 Next.js 是支持。
应用: 匹配 404 页面
捕获所有不配路由(即 404 页面)
方式一: [...404].vue
:在根目录或其子目录中添加[...404].vue
页面。
方式二: error.vue
:在根目录中添加error.vue
页面。
<script setup lang="ts">
const props = defineProps({
error: Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
<template>
<div>
<h2>{{ error.statusCode }}</h2>
<button @click="handleError">清除错误</button>
</div>
</template>
error 对象提供以下字段:
{
url: string
statusCode: number
statusMessage: string
message: string
description: string
data: any
}
路由匹配规则
优先级: 预定义路由 > 动态路由 > 全匹配路由
- 预定义路由:
pages/detail/create.vue
- 匹配
/detail/create
- 匹配
- 动态路由:
pages/detail/[id].vue
- 匹配
/detail/1
,/detail/abc
等 - 不匹配
/detail/create
、/detail/1/1
、/detail/
等
- 匹配
- 全匹配路由:
pages/detail/[...slug].vue
- 匹配
/detail/1/2
,/detail/a/b/c
等 - 不匹配
/detail
- 匹配
嵌套路由
Nuxt 和 Vue 一样,也是支持嵌套路由的,只不过在 Nuxt 中,嵌套路由也是根据目录结构和文件的名称自动生成。
嵌套路由步骤:
1、~pages/parent.vue
:创建一个一级路由。
- 在
~pages/parent.vue
中添加<NuxtPage/>
路由占位。
2、~pages/parent/
:创建一个与一级路由同名同级的文件夹。
3、在 parent 文件夹下,创建一个嵌套的二级路由。
~pages/parent/child.vue
:二级路由页面。~pages/parent/index.vue
:二级路由默认页面。
目录结构:
-| pages/
---| parent.vue
---| parent/
------| child.vue
路由结构:
路由中间件
- definePageMeta():
({middleware})
,是一个编译器宏,你可以用它为位于pages/
目录中的页面组件设置元数据。- middleware:
[]
,直接定义匿名或命名中间件。 - :``,
- 返回:
- :``,
- middleware:
文档:https://nuxt.com.cn/docs/guide/directory-structure/middleware
Nuxt 提供了一个可定制的 路由中间件,用来监听路由的导航。包括局部和全局监听。支持服务端和客户端。
路由中间件分为三种:
- 匿名(内联)路由中间件:在页面中使用 definePageMeta() 函数定义,可监听局部路由。当注册多个中间件时,会按照注册顺序来执行。
- 命名路由中间件:在 middleware 目录下定义,并会自动加载中间件。命名规范 kebab-case。
- 全局路由中间件:在 middleware 目录中,需带
.global
后缀的文件,每次路由更改都会自动运行。优先级比前面的高,支持两端。
匿名(内联)路由中间件:
在页面中使用 definePageMeta() 函数定义,可监听局部路由。当注册多个中间件时,会按照注册顺序来执行。
definePageMeta({
// 将中间件定义为函数
middleware: [
function (to, from) { // 没有next参数
const auth = useState('auth')
if (!auth.value.authenticated) {
// return返回 null、''或没有return时,会继续执行下一个中间件
// return返回 navigateTo()路由跳转时,直接跳转到指定页面。不会执行下一个中间件
return navigateTo('/login')
}
if (to.path !== '/checkout') {
return navigateTo('/checkout')
}
}
],
// ... 或者设置为字符串
middleware: 'auth'
// ... 或者设置为多个字符串
middleware: ['auth', 'another-named-middleware']
})
命名路由中间件:
在 middleware 目录下定义,并会自动加载中间件。命名规范 kebab-case。
1、在~/middleware
目录中通过 defineNuxtRouterMiddleware() 定义命名的中间件home.ts
。
注意: 执行时机:
- server:刷新浏览器时会在服务端执行中间件。
- client:切换路由时,只会在客户端执行。
2、在某个页面中通过 definePageMeta({middleware}) 使用定义的命名中间件。
全局路由中间件:
在 middleware 目录中,需带 .global
后缀的文件,每次路由更改都会自动运行。优先级比前面的高,支持两端。
1、在~/middleware
目录中通过 defineNuxtRouterMiddleware() 定义命名的中间件auth.global.ts
。
2、此时该中间件已经在全局适用。每次路由跳转时都会执行一次该中间件。
路由验证
Nuxt 支持对每个页面路由进行验证,我们可以通过 definePageMeta() 中的 validate 属性来对路由进行验证。
- validate 属性接受一个回调函数,回调函数中以 route 作为参数
- 回调函数的返回值支持:
- 返回 bool 值来确定是否放行路由 ➢ true 放行路由 ➢ false 默认重定向到内置的 404 页面
- 返回对象 ➢ { statusCode:401 } // 返回自定义的 401 页面,验证失败
1、使用definePageMeta() 中的 validate 属性验证路由是否符合规则
2、如果验证失败,返回错误码statusCode
、statusMessage
注意: statusMessage 不支持中文字符串。
3、在项目根目录(不是 pages 目录)新建 error.vue
文件,处理错误。
4、清除错误信息:在error.vue
页面通过 clearError() 方法清除错误信息并跳转到指定页面。
Layout
Layout 布局是页面的包装器,可以将多个页面共性东西抽取到 Layout 布局 中,如页眉和页脚。
布局是使用 <slot />
组件来显示页面内容的 Vue 文件。
Layout 布局有两种使用方式:
方式一:默认布局
在 layouts 目录下新建默认的布局组件,比如:
layouts/default.vue
。名字必须是default.vue
。然后在 app.vue 中通过
<NuxtLayout>
内置组件来使用。
方式二:自定义布局
在 layouts 目录下新建 Layout 布局组件,比如:
layouts/custom-layout.vue
。名字自定义。在
app.vue
中给<NuxtLayout>
内置组件不要指定 name 属性。在
login.vue
页面中使用 definePageMeta() 宏函数来指定 layout 布局。
渲染模式
浏览器 和 服务器都可以解释 JavaScript 代码,都可以将 Vue.js 组件呈现为 HTML 元素。此过程称为渲染。
- 客户端渲染:在客户端将 Vue.js 组件呈现为 HTML 元素。
- 服务器渲染:在服务器将 Vue.js 组件呈现为 HTML 元素。
Nuxt3 支持多种渲染模式:
客户端渲染(CSR): 只需将 ssr 设置为 false
jsexport default defineNuxtConfig({ ssr: false })
服务器端渲染(SSR):只需将 ssr 设置为 true
jsexport default defineNuxtConfig({ ssr: true })
混合渲染(SSR | CSR | SSG | SWR):需在 routeRules 根据每个路由动态配置渲染模式(beta 版本)
jsexport default defineNuxtConfig({ routeRules: { // 主页在构建时预渲染 '/': { prerender: true }, // 产品静态页面按需生成,后台自动重新验证 '/products/**': { swr: 3600 }, // 博客文章按需生成,直到下一次部署前持续有效 '/blog/**': { isr: true }, // 管理仪表板仅在客户端渲染 '/admin/**': { ssr: false }, // 在API路由上添加cors头 '/api/**': { cors: true }, // 跳转旧的URL '/old-page': { redirect: '/new-page' } } })
插件
API:
useNuxtApp():
()
,提供了一种访问 Nuxt 共享运行时上下文的方式,该上下文在客户端和服务器端都可用。它帮助你访问 Vue 应用程序实例、运行时钩子、运行时配置变量和内部状态,如ssrContext
和payload
。- 返回:
- nuxtApp:
{nuxtApp, $config, $pinia,自定义}
,返回一个包含 Nuxt 应用实例的对象。- nuxtApp:``,表示 Nuxt 应用的实例。
- $config:``,应用的运行时配置。
- $pinia:``,Pinia 实例,用于状态管理。
- 自定义:``,自定义的提供的属性和方法,如通过插件提供的 API。
属性
nuxtApp.vueApp:
()
,用于访问 Vue 应用实例的属性。提供了与 Vue 应用的更直接的交互能力。返回:
vueApp:
App
,vue 的实例对象 app。jsexport default defineNuxtPlugin((nuxtApp) => { // 注册全局组件 nuxtApp.vueApp.component('MyGlobalComponent', MyGlobalComponent) // 注册全局指令 nuxtApp.vueApp.directive('focus', { mounted(el) { el.focus() } }) // 集成其他库 const pinia = createPinia() nuxtApp.vueApp.use(pinia) })
方法
nuxtApp.provide():
(key, value)
,用于将数据或功能注入到应用程序中的一个方法。此处用于注册插件。这是通过 Vue 3 的依赖注入机制实现的,允许你在组件树中共享数据而无需逐层传递 props。key:
string
,表示要提供的名称。value:
any
,表示要注入的值。js// 1. 注册插件 export default defineNuxtPlugin((nuxtApp) => { const myFunction = () => { console.log('Hello from myFunction!') } // 注入插件函数到应用中 nuxtApp.provide('myFunction', myFunction) })
js// 2. 在组件中使用注册的插件 import { useNuxtApp } from '#app' const { $myFunction } = useNuxtApp() const callMyFunction = () => { $myFunction() // 将会输出 "Hello from myFunction!" }
nuxtApp.hook():
(name, callback)
,用于注册钩子函数的方法。name:
string
,钩子的名称,表示在什么事件触发时会执行该回调函数。callback:
function
,当钩子被触发时调用的回调函数。jsexport default defineNuxtPlugin((nuxtApp) => { // App created hook nuxtApp.hook('app:created', () => { console.log('Application has been created.') }) // App mounted hook nuxtApp.hook('app:mounted', () => { console.log('Application has been mounted.') }) // Route before each hook nuxtApp.hook('route:beforeEach', (to, from) => { console.log(`Navigating from ${from.fullPath} to ${to.fullPath}`) }) })
nuxtApp.callHook():
(hookName, ...args)
,用于手动触发钩子函数的方法。hookName:
string
,表示需要调用的钩子的名称。...args:
any
,传递给钩子回调函数参数的数据。jsexport default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('route:beforeEach', (to, from) => { if (to.path === '/special') { // 手动调用其他钩子 nuxtApp.callHook('special-route-enter', { route: to }) } }) nuxtApp.hook('special-route-enter', ({ route }) => { console.log(`Entering special route: ${route.fullPath}`) }) })
defineNuxtPlugin():
(fn | obj)
,用于创建和注册插件的函数。fn:
(nuxtApp) => void
,函数式写法。obj:
{name, enforce, setup, hooks, env}
,对象式写法。js// 函数式写法 export default defineNuxtPlugin((nuxtApp) => { // 在这里可以设置你的插件逻辑 // 例如,向 nuxtApp 注入一个全局的 API nuxtApp.provide('myApi', () => { return { fetchData: async () => { // 模拟 API 调用 return await fetch('/api/data').then((res) => res.json()) } } }) })
js// 对象式写法 export default defineNuxtPlugin({ name: 'my-plugin', enforce: 'pre', // 或 'post' async setup(nuxtApp) { // 这相当于一个普通的功能性插件 }, hooks: { // 这里可以直接注册Nuxt应用运行时钩子 'app:created'() { const nuxtApp = useNuxtApp() // } }, env: { // 如果不希望插件在仅渲染服务器端或孤立组件时运行,请将此值设置为`false`。 islands: true } })
创建插件:
Nuxt3 支持自定义插件进行扩展。
useNuxtApp()提供了访问 Nuxt 共享运行时上下文的方法和属性(两端可用):provide、hooks、callhook、vueApp 等。
方式一:使用 useNuxtApp() 中的 provide(name, value) 方法在 app.vue
中直接创建。
在
app.vue
中创建插件可以在
app.vue
或其他页面中访问注册的插件
方式二:在 plugins
目录中通过 defineNuxtPlugin() 创建插件(推荐)
1、在 plugins 目录下使用defineNuxtPlugin()
创建 plugins/price.ts
插件,参数是一个回调函数。
2、然后在组件中使用 useNuxtApp()
创建的实例对象nuxtApp.$xxx
来访问插件中的方法。
自动注册:
plugins
里面的所有插件都会自动注册,你不需要单独将它们添加到你的nuxt.config
中。只有
plugins
目录的顶层文件或任何子目录中的索引文件才会自动注册为插件。bash-| plugins/ ---| foo.ts // 被扫描 ---| bar/ -----| baz.ts // 不被扫描 -----| foz.vue // 不被扫描 -----| index.ts // 目前被扫描,但已弃用
区分环境:可以在文件名中使用.server
或.client
后缀,只在服务器端或客户端上加载插件。如price.client.ts
:
注册顺序:可以通过在文件名前面添加“字母”编号来控制插件的注册顺序。
plugins/
| - 01.myPlugin.ts
| - 02.myOtherPlugin.ts
生命周期
API
nuxtApp.hook():
(name, callback)
,用于注册钩子函数的方法。name:
string
,钩子的名称,表示在什么事件触发时会执行该回调函数。callback:
function
,当钩子被触发时调用的回调函数。jsexport default defineNuxtPlugin((nuxtApp) => { // App created hook nuxtApp.hook('app:created', () => { console.log('Application has been created.') }) // App mounted hook nuxtApp.hook('app:mounted', () => { console.log('Application has been mounted.') }) // Route before each hook nuxtApp.hook('route:beforeEach', (to, from) => { console.log(`Navigating from ${from.fullPath} to ${to.fullPath}`) }) })
nuxtApp.callHook():
(hookName, ...args)
,用于手动触发钩子函数的方法。hookName:
string
,表示需要调用的钩子的名称。...args:
any
,传递给钩子回调函数参数的数据。jsexport default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('route:beforeEach', (to, from) => { if (to.path === '/special') { // 手动调用其他钩子 nuxtApp.callHook('special-route-enter', { route: to }) } }) nuxtApp.hook('special-route-enter', ({ route }) => { console.log(`Entering special route: ${route.fullPath}`) }) })
App 生命周期
App Hooks主要由Nuxt 插件用于挂接渲染生命周期,但也可在 Vue 组合式中使用。
语法:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
/* 在这里写入你的代码 */
})
})
app 生命周期钩子:
钩子 | 参数 | 环境 | 描述 |
---|---|---|---|
app:created | vueApp | 服务器端和客户端 | 在初始 vueApp 实例创建时调用。 |
app:error | err | 服务器端和客户端 | 在发生致命错误时调用。 |
app:error:cleared | { redirect? } | 服务器端和客户端 | 在发生致命错误时调用。 |
app:data:refresh | keys? | 服务器端和客户端 | (内部) |
vue:setup | - | 服务器端和客户端 | (内部) |
vue:error | err, target, info | 服务器端和客户端 | 当 vue 错误传播到根组件时调用。了解更多。 |
app:rendered | renderContext | 服务器端 | 在 SSR 渲染完成时调用。 |
app:redirected | - | 服务器端 | 在 SSR 重定向之前调用。 |
app:beforeMount | vueApp | 客户端 | 在应用程序挂载之前调用,仅在客户端端调用。 |
app:mounted | vueApp | 客户端 | 在 Vue 应用程序初始化并在浏览器中挂载时调用。 |
app:suspense:resolve | appComponent | 客户端 | 在 Suspense 解析事件上调用。 |
link:prefetch | to | 客户端 | 当观察到 <NuxtLink> 被预取时调用。 |
page:start | pageComponent? | 客户端 | 在 Suspense 挂起事件上调用。 |
page:finish | pageComponent? | 客户端 | 在 Suspense 解析事件上调用。 |
page:transition:finish | pageComponent? | 客户端 | 在页面过渡的 onAfterLeave 事件之后调用。 |
示例: 在plugins
目录的插件中监听 hooks。
在组件的 setup 标签中的 hooks 无法监听 app:create
、app:beforeMount
和 vue:setup
,因为此时这些 hooks 已经触发完毕。
组件生命周期
组件生命周期语法与Vue一样。
因为没有任何动态更新,所以像 onMounted
或者 onUpdated
这样的生命周期钩子不会在 SSR 期间被调用,而只会在客户端运行。
你应该避免在 setup()
或者 <script setup>
的根作用域中使用会产生副作用且需要被清理的代码。这类副作用的常见例子是使用 setInterval
设置定时器。我们可能会在客户端特有的代码中设置定时器,然后在 onBeforeUnmount
或 onUnmounted
中清除。然而,由于 unmount 钩子不会在 SSR 期间被调用,所以定时器会永远存在。为了避免这种情况,请将含有副作用的代码放到 onMounted
中。
组件生命周期钩子在客户端和服务端的触发情况:
钩子 | 钩子(组合式) | 客户端 | 服务端 |
---|---|---|---|
beforeCreate | - | ✅ | ✅ |
setup | ✅ | ✅ | |
created | - | ✅ | ✅ |
beforeMount | onBeforeMount | ✅ | ❌ |
mounted | onMounted | ✅ | ❌ |
beforeUpdate | onBeforeUpdate | ✅ | ❌ |
updated | onUpdated | ✅ | ❌ |
beforeDestroy | onBeforeUnmount | ✅ | ❌ |
destroyed | onUnmounted | ✅ | ❌ |
网络请求
API
注意: 以下函数只能用在 setup
或 生命周期钩子
中使用。
- $fetch():
()
,- :``,
- :``,
- 返回:
- :``,
- useFetch():
()
,- :``,
- :``,
- 返回:
- :``,
- useAsyncData():
()
,- :``,
- :``,
- 返回:
- :``,
- useLazyFetch():
()
,- :``,
- :``,
- 返回:
- :``,
- useLazyAsyncData():
()
,- :``,
- :``,
- 返回:
- :``,
获取数据
方式一:$fetch()用于在Vue应用程序或API路由中进行HTTP请求。
问题: 在组件中使用 $fetch() 而不使用useAsyncData进行包装会导致发送两次数据请求。
解决:建议在获取组件数据时使用useFetch()或 useAsyncData() + $fetch()来防止重复获取数据。
方式二:useAsyncData() + $fetch()
发送请求时机:
useAsyncData()在刷新页面时在服务端发送请求,客户端不发送。
useAsyncData()在站内路由切换时在服务端不会发送请求,客户端发送请求。
保证key值唯一:
当key值重复,页面刷新(此时是服务端发起的请求,客户端通过水合得到数据)时请求到的数据也会重复。
当key值重复,站内路由跳转(此时是客户端发起的请求)时,请求到的数据不会重复。
方式三:useFetch() useAsyncData() + $fetch()
的简写。(推荐)
请求选项:
type UseFetchOptions<DataT> = {
key?: string
method?: string
query?: SearchParams
params?: SearchParams
body?: RequestInit['body'] | Record<string, any>
headers?: Record<string, string> | [key: string, value: string][] | Headers
baseURL?: string
server?: boolean
lazy?: boolean
immediate?: boolean
getCachedData?: (key: string) => DataT
deep?: boolean
default?: () => DataT
transform?: (input: DataT) => DataT
pick?: string[]
watch?: WatchSource[] | false
}
拦截器:
const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
onRequest({ request, options }) {
// 设置请求头
options.headers = options.headers || {}
options.headers.authorization = '...'
},
onRequestError({ request, options, error }) {
// 处理请求错误
},
onResponse({ request, response, options }) {
// 处理响应数据
localStorage.setItem('token', response._data.token)
// response._data的数据会返回出去给{ data }
// return返回的数据不会返回出去给{ data }
},
onResponseError({ request, response, options }) {
// 处理响应错误
}
})
懒加载: useFetch()
默认会阻塞页面的导航。可以通过lazy
选项控制是否懒加载。
通过
watch()
监听获取异步加载的数据。通过调用
refresh()
刷新客户端重新发送请求。通过pending
显示网络请求所处状态。通过修改请求参数,重新发送网络请求。
方式四:useLazyFetch()是useFetch()
懒加载模式的简写。
useFetch VS axios
获取数据 Nuxt 推荐使用 useFetch 函数,为什么不是 axios ?
useFetch
底层调用的是$fetch
函数,该函数是基于 unjs/ofetch 请求库,并与原生的 Fetch API 有者相同 API。- unjs/ofetch 是一个跨端请求库: A better fetch API. Works on node, browser and workers。
- 如果运行在服务器上,它可以智能的处理对 API 接口的直接调用。
- 如果运行在客户端上,它可以对后台提供的 API 接口正常的调用(类似 axios),当然也支持第三方接口的调用。
- 会自动解析响应和对数据进行字符串化。
- useFetch 支持智能的类型提示和智能的推断 API 响应类型。
- 在 setup 中用 useFetch 获取数据,会减去客户端重复发起的请求。
useFetch 封装
1、定义 HYRequest 类,并定义request方法。
2、在类中定义 get、post 方法
3、在 request 中使用 useFetch 发起网络请求
4、添加 TypeScript 类型声明
服务端接口
Nuxt3 提供了编写后端服务接口的功能,编写服务接口可以在 server/api
目录下编写。
语法:
文件系统路由: 存放在以下目录中的文件遵循文件系统路由。
~/server/api
:这里的文件会在路由中添加/api
前缀。~/server/routes
:这里的文件不会在路由中添加/api
前缀。
API: 通过 defineEventHandler() 定义API接口。
区分请求方法: 为文件名添加.get
后缀。
实现过程: 编写一个 /api/homeinfo
接口
1、在 server/api
目录下新建 homeinfo.ts
,并使用 defineEventHandler() 函数定义接口(支持 async)。
2、在浏览器中通过http://localhost:3000/api/homeInfo
访问该接口。
3、在组件中使用 useFetch()
函数调用/api/homeinfo
接口。
示例: 添加GET请求
示例: 添加POST请求,模拟登录
1、在~/server/api/login.post.ts
中添加登录接口
2、在登录组件中发起登录请求,并保存返回的token到cookie中,之后跳转到首页。
3、在首页中获取保存的cookie信息
状态管理
useState()
Nuxt 跨页面、跨组件全局状态共享可使用 useState(支持 Server 和 Client ):
语法:
参数:
- init:为状态提供初始值的函数,该函数也支持返回一个 Ref 类型
- key: 唯一 key,确保在跨请求获取该数据时,保证数据的唯一性。为空时会根据文件和行号自动生成唯一 key
返回值: Ref 响应式对象
基本使用:
1、在 composables
目录下创建useCounter.ts
,并通过useState()
定义需全局共享的状态并导出。
hook名由文件名决定。
hook名由导出的命名函数名决定。
2、在组件中导入 states.ts 导出的全局状态。
其他类型值:
对象类型:
注意:
- useState 只能用在 setup 函数 和 Lifecycle Hooks 中。
- useState 不支持
class
,function
orsymbol
类型,因为这些类型不支持序列化。
Pinia
集成Pinia
依赖包:
@pinia/nuxt:会处理 state 同步问题,比如不需要关心序列化或 XSS 攻击等问题。
- 安装:
npm install @pinia/nuxt –-save
- 安装:
pinia:如有遇到 pinia 安装失败,可以添加
--legacy-peer-deps
或--force
告诉 NPM 忽略对等依赖并继续安装。或使用 yarn。- 安装:
npm install pinia –-save
- 安装:
基本使用:
1、安装依赖包。
2、在 nuxt.config
文件中添加: modules: ['@pinia/nuxt']
3、创建pinia模块useHomeStore
4、在组件中获取store中的数据
TS类型
异步请求
1、在store中发送异步请求
2、在组件中触发store中的异步action
对比useState()
Nuxt 跨页面、跨组件全局状态共享,既可以使用 useState,也可以使用 Pinia,那么他们有什么异同呢?
共同点:
- 都支持全局状态共享,共享的数据都是响应式数据
- 都支持服务器端和客户端共享
Pinia优势: Pinia 比 useState 有更多的优势
- 开发工具Devtools支持:
- 跟踪动作,更容易调试
- store 可以出现在使用它的组件中
- 模块热替换:
- 无需重新加载页面即可修改 store 数据
- 在开发时保持任何现有状态
- 插件:可以使用插件扩展 Pinia 功能
- TypeScript支持:提供适当的 TypeScript 支持或自动完成